﻿using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Guru.Web.Controls.Validators;
using System;

namespace Guru.Web.Controls.Validators
{
    /// <summary>
    /// jQuery validation code generator
    /// </summary>
    /// @Author:HungDinh
    public class Validator : WebControl, INamingContainer
    {
        #region Const 
        protected const string _TAG_INPUT_STATE = @"<div style='display:none'>
            <input id='{0}'  type='text' name='{0}'>
        </div>";

        protected const string _JS_SCRIPT_BLOCK = "<script src=\"{0}\" type=\"text/javascript\"></script>";

        protected const string _JS_TAG_OPEN = "<script type=\"text/javascript\">";
        protected const string _JS_TAG_CLOSE = "</script>";

        /// <summary>
        /// Config for summary message
        ///     {0}: ClientId of the messge container
        /// </summary>
        protected const string _JS_SUMMARY_INVALID = @"
            ,errorLabelContainer: $('#{0}'), wrapper: 'li'";

        /// <summary>
        ///  Config for alert message
        ///     {0}: formid
        /// </summary>
        protected const string _JS_ALERT_INVALID = @"
        ,invalidHandler: function(form, validator) [OPEN] [CLOSE],
        errorPlacement: function(error, element)[OPEN]
             var validator1 = $('#{0}').data('validator');
                if (validator1 != null && IsShownMessageBox == 0) [OPEN] 
                    var errors = validator1.numberOfInvalids();
                    if (errors) [OPEN] 
                        IsShownMessageBox = 1;
                        alert(validator1.errorList[0].message);
                        try[OPEN]
                        validator1.errorList[0].element.focus();
                        [CLOSE]catch(ex)[OPEN][CLOSE]

                    [CLOSE]
               [CLOSE]
        [CLOSE]";

        /// <summary>
        /// Ready function
        ///     {0}: FormId
        ///     {1}: Value of AllowValidatingOnFocusOut
        ///     {2}: AllowValidatingOnKeyUp
        ///     {3}: Clear message on focus
        ///     {4}: Message type config
        /// </summary>
        protected const string _JS_FN_READY = @"  
         var IsShownMessageBox = 0;        
        $(document).ready(
            function()[OPEN]    
                $('#{0}').validate([OPEN] 
                    onsubmit: false, onfocusout: {1}, onkeyup:{2}, focusCleanup: {3}, onclick: false
                    //Message type                    
                    {4}
            [CLOSE]);                    
        [CLOSE]);
        ";
       
       
        /// <summary>
        /// The validation function
        ///     {0}: Name of validation function
        ///     {1}: Form ID
        ///     {2}: Validation Rules
        ///     {3}: Validation instruction
        ///     {4}: Remove rules
        /// </summary>
        protected const string _JS_FN_VALIDATE = @"  
        //Validate for group {0}          
        function {0} (isCleanMessage) [OPEN]
            //Clear previous error message   
            if(isCleanMessage==null || isCleanMessage==true)
            [OPEN]
                var validator=$('#{1}').data('validator');         
                if(validator!=null) validator.resetForm();
                if($('.error')) $('.error').removeClass('error');
            [CLOSE]
            IsShownMessageBox = 0;

            //Add rule
            {2}

            // Validate the form and retain the result.
            var isValid = true;  
            {3}  
            
            //Remove rule
            {4}      
            return (isValid==false) ? false : true; 
        [CLOSE]            
        ";
        #endregion

        #region Members
        protected ValidationItemCollection _Items = null;
       

        #endregion

        #region Init
        public Validator(): base() {
            MessageType = Guru.Web.Controls.Validators.MessageType.Inline;
            this._Items = new ValidationItemCollection();
            this._Items.Container = this;

        }
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (!base.DesignMode)
            {
                if (this.GetFirstElement() == null)
                    Page.Items[typeof(Validator)] = this;
            }
        }

       
        #endregion

        #region Properties
        /// <summary>
        /// Allows validating on focus out
        /// Default: false
        /// </summary>
        [Category("Validator"), DefaultValue(false),
        Description("Allows validating on focus out")]
        public bool AllowValidatingOnFocusOut
        {
            get
            {
                string validate = (string)this.ViewState["AllowValidatingOnFocusOut"];
                if (string.IsNullOrEmpty(validate))
                    return false;
                bool allowValidate = false;
                bool.TryParse(validate, out allowValidate);
                return allowValidate;
            }
            set { this.ViewState["AllowValidatingOnFocusOut"] = value; }
        }

        /// <summary>
        /// Allows validating on key up
        /// Default: false
        /// </summary>
        [Category("Validator"), DefaultValue(false),
        Description("Allows validating on key up")]
        public bool AllowValidatingOnKeyUp
        {
            get
            {
                string validate = (string)this.ViewState["AllowValidatingOnKeyUp"];
                if (string.IsNullOrEmpty(validate))
                    return false;
                bool allowValidate = false;
                bool.TryParse(validate, out allowValidate);
                return allowValidate;
            }
            set { this.ViewState["AllowValidatingOnKeyUp"] = value; }
        }

        /// <summary>
        /// Hide validation message on focus
        /// Default: false
        /// </summary>
        [Category("Validator"), DefaultValue(false),
        Description("Hide validation message on focus")]
        public bool HideMessageOnFocus
        {
            get
            {
                string validate = (string)this.ViewState["AllowValidatingCleanUp"];
                if (string.IsNullOrEmpty(validate))
                    return false;
                bool allowValidate = false;
                bool.TryParse(validate, out allowValidate);
                return allowValidate;
            }
            set { this.ViewState["AllowValidatingCleanUp"] = value; }
        }

        /// <summary>
        /// Asp.net form name
        /// </summary>
        [Category("Validator"), DefaultValue("aspnetForm"),
        Description("Asp.net FormID")]
        internal string FormID {
            get
            {                
                return this.Page.Form.ClientID;
            }
            
        }


        /// <summary>
        /// MessageContainerClientID
        /// Not take affect until MessageType=Summary
        /// </summary>
        [Category("Validator"), DefaultValue(""),
        Description("MessageContainerClientID")]
        public string MessageContainerClientID
        {
            get
            {
                string formName = (string)this.ViewState["MessageContainerClientID"];
                return formName;
            }
            set { this.ViewState["MessageContainerClientID"] = value; }
        }

        /// <summary>
        /// Validation Group        
        /// </summary>
        [Category("Validator"), DefaultValue(""),
        Description("ValidationGroup")]
        public string ValidationFunction
        {
            get
            {
                if (this.ViewState["ValidationFunction"] != null)
                {
                    return (string)this.ViewState["ValidationFunction"];
                }
                else
                {
                    return "";
                }
            }
            set { this.ViewState["ValidationFunction"] = value; }
        }

        /// <summary>
        /// Use dynamic function name
        /// </summary>
        [Category("Validator"), DefaultValue(""),
        Description("DynamicValidationFunction")]
        public string DynamicValidationFunction
        {
            get
            {
                if (this.ViewState["DynamicValidationFunction"] != null)
                {
                    return (string)this.ViewState["DynamicValidationFunction"];
                }
                else
                {
                    return "";
                }
            }
            set { this.ViewState["DynamicValidationFunction"] = value; }
        }
        [Category("Validator"), DefaultValue(""),
        Description("DynamicValidationFunction")]
        public string ClientValidationFunction
        {
            get
            {
                return string.IsNullOrEmpty(DynamicValidationFunction) ? ValidationFunction : UniqueID + "_" + DynamicValidationFunction;
            }
            
        }
        /// <summary>
        /// User message type: alertbox, message summary section, message inline
        /// </summary>
        [Category("Validator"), DefaultValue(MessageType.AlertBox),
        Description("User message type: alertbox, message summary section, message inline")]       
        public MessageType MessageType
        {
            get
            {
                object messageType = this.ViewState["MessageType"];
                if (messageType == null) return MessageType.AlertBox;
                return (MessageType)Enum.Parse(typeof(MessageType), messageType.ToString());
            }
            set { this.ViewState["MessageType"] = value; }
        }
       
        /// <summary>
        /// Contains the items which were assigned to the validator
        /// </summary>        
        [Category("Misc"), Description("Items of the validation"), Bindable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ValidationItemCollection Items{
            get { return this._Items; }
        }

       
        #endregion

        #region Rendering

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            string jQueryValidate = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Guru.Web.Controls.EmbeddedResource.jquery.validate.js");
            string addMethods = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Guru.Web.Controls.EmbeddedResource.additional-methods.js");
            string ddsAddMethods = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Guru.Web.Controls.EmbeddedResource.as-additional-methods.js");
            
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "jquery.validate.js", string.Format(_JS_SCRIPT_BLOCK, jQueryValidate));
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "additional-methods.js", string.Format(_JS_SCRIPT_BLOCK, addMethods));
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "as-additional-methods.js", string.Format(_JS_SCRIPT_BLOCK, ddsAddMethods));

           
            //Render message 
            string messageConfigs = string.Empty;
            if (this.MessageType == MessageType.AlertBox)
                messageConfigs = string.Format(_JS_ALERT_INVALID, this.FormID);
            else if (this.MessageType == MessageType.Summary)
                messageConfigs = string.Format(_JS_SUMMARY_INVALID, this.MessageContainerClientID);          
                        
            string jQueryReady = string.Format(_JS_FN_READY, this.FormID
                  , this.AllowValidatingOnFocusOut.ToBoolean()
                  , this.AllowValidatingOnKeyUp.ToBoolean()
                  , this.HideMessageOnFocus.ToBoolean()
                  , messageConfigs);
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "jQuery-validation-init", jQueryReady.ReplaceTag(), true);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            //FormName cannot be null
            if (string.IsNullOrEmpty(this.FormID))
                throw new PropertyNullRefException("Validator.FormID"); 
           
            string validFunctions = this.GenerateValidateFunction(writer); 
            validFunctions = validFunctions.ReplaceTag();
            
            //Render
            writer.Write(_JS_TAG_OPEN);
            writer.Write(validFunctions);
            writer.Write(_JS_TAG_CLOSE);

        }

        private string GenerateValidateFunction(HtmlTextWriter writer)
        {
            //throw compiled error for dev
            if (string.IsNullOrEmpty(this.ClientValidationFunction))
                throw new PropertyNullRefException(this.ID + ".ValidationFunction and " + this.ID + ".DynamicValidationFunction");

            string validInstruction = string.Empty;
            string jQueryRules = string.Empty;
            string jQueryRulesRemove = string.Empty;
            string additionalMethods = string.Empty;

            foreach (ValidationItem item in this.Items)                   
                if (item.ObjectControlToValidate.Visible)
                {
                                      
                    jQueryRules += item.RenderRuleRegister(writer);

                    string instruction = item.RenderValidIntruction(writer);
                    if (validInstruction.Contains(instruction) == false)
                        validInstruction += item.RenderValidIntruction(writer);

                    string rule = item.RenderRuleRemove(writer);
                    if (jQueryRulesRemove.Contains(rule) == false)
                        jQueryRulesRemove += item.RenderRuleRemove(writer);

                    additionalMethods += item.RenderAdditionalMethods(writer);
                }

            string validFunctions = string.Format(_JS_FN_VALIDATE, this.ClientValidationFunction, this.FormID,
                jQueryRules, validInstruction, jQueryRulesRemove);
            string jQueryCode = string.Format("{0} \r\n {1}", additionalMethods, validFunctions);
            return jQueryCode.ReplaceTag();
        }
        #endregion

        public bool EqualTo(Validator val) {
            if (val.ClientID == this.ClientID) return true;
            else return false;
        }

        public Validator GetFirstElement()
        {
            if (this.Page == null)
            {
                throw new ArgumentNullException("page");
            }
            return (this.Page.Items[typeof(Validator)] as Validator);
        }


    }   
    
}
